#include <iostream>
using std::cout;
using std::endl;

#include <string>
using std::string;

#include <set>
#include <deque>
using std::deque;

#include "../sqlite++/sqlite++.h"
using namespace tbd;

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

#define TBD_SYMBOL(id) struct id {static const char* value() {return #id;}}

template<class T, class S, class D> struct table_column {
  table_column() { cout << "column:" << S::value() << endl; }
};
template<class T, class S, class D> struct table_key {
  table_key() { cout << "primary key:" << S::value() << endl; }
};
template<class T, class S, class D> struct table_has_a {
  table_has_a() { cout << "has a:" << S::value() << endl; }
};
template<class T, class S, class D> struct table_has_many {
  table_has_many() { cout << "has many:" << S::value() << endl; }
};
template<class T, class S, class D> struct table_belongs_to {
  table_belongs_to() { cout << "belongs to:" << S::value() << endl; }
};

namespace sym {
  TBD_SYMBOL(index);
  TBD_SYMBOL(parent);
}

template<class T> struct acts_as_list {
  table_column<T, sym::index, int> index;
};

template<class T> struct acts_as_tree {
  table_belongs_to<T, sym::parent, T> index;
};

template<class T, class ST> struct table {
  table() { cout << "table:" << ST::value() << endl; }
  template<class S, class D> struct primary_key: table_key<T, S, D> {};
  template<class S, class D> struct column: table_column<T, S, D> {};
  template<class S, class D> struct has_a: table_has_a<T, S, D> {};
  template<class S, class D> struct has_many: table_has_many<T, S, D> {};
  template<class S, class D> struct belongs_to: table_belongs_to<T, S, D> {};

  static T*& current_instance() {
    static T* instance = 0;
    return instance;
  }
protected:
 
};

template<class T> struct not_null {};

namespace sym {
  TBD_SYMBOL(user);
  TBD_SYMBOL(id);
  TBD_SYMBOL(name);

  TBD_SYMBOL(forum);
  TBD_SYMBOL(description);

  TBD_SYMBOL(thread);
  TBD_SYMBOL(title);

  TBD_SYMBOL(comment);
  TBD_SYMBOL(content);
}

struct user: table<user, sym::user> { 
  primary_key <sym::id, int> id;
  column      <sym::name, not_null<std::string> > name;
};

struct forum: table<forum, sym::forum> { 
  primary_key <sym::id, int> id;
  column      <sym::description, std::string > description;
  has_many    <sym::forum, struct thread> threads;
};

struct thread: table<thread, sym::thread>, acts_as_list<thread> {
  primary_key <sym::id, int> id;
  column      <sym::title, std::string> title;
  belongs_to  <sym::user, user> author;
  belongs_to  <sym::forum, forum> parent;
  has_a       <sym::thread, struct comment> root_comment;
};

struct comment: table<comment, sym::comment>, acts_as_tree<comment> {
  primary_key <sym::id, int> id;
  belongs_to  <sym::user, user> author;
  belongs_to  <sym::thread, thread> parent;
  column      <sym::content, std::string> content;
};

int main() {
  thread hugo;

  try {

    database conn("test.db");

// Execute a couple of simple queries

    try {
      conn.execute("drop table t");
    } catch ( db_exception & e ) {
      std::cerr << e.what() << std::endl;
    }

    conn.execute("create table t ( a int, b varchar(10), c double, "
            "d_is_an_incredibly_long_column_name int )");

    // Execute a parameterized query

    db_value<double> c;      // a value that may be null

    // prepare the statement, so get to be able
    // to execute it in a loop with different values

    command insert(conn, "insert into t values ( ?, ?, ?, ? )");

    char * nums[] = { "zero", "one", "two", "three", "four", "five", "six",
      "seven", "eight", "nine" }; 
    for (long a = 0; a < 10; ++a) {
      c = (a % 2) ? db_double(a * 1.1) : null;
      insert << a | nums[a] | c | null << exec;
    }

// Execute a query that returns a result set

    typedef query<int, string, db_double, db_int> t_query;
    typedef t_query::row::accessor<0> row_a;
    typedef t_query::row::accessor<1> row_b;
    typedef t_query::row::accessor<2> row_c;
    typedef t_query::row::accessor<3> row_d;

    t_query rs(conn, "select * from t");

    // Explore the structure of the result set

    std::cout << "No. of columns: " << rs.column_count() << '\n';
    for ( int i = 0; i < rs.column_count(); ++i ) {
      std::cout << rs.column_name(i); 
//    << "\t " << rs[i].type().name() << '(' << rs[i].size() << ", " << rs[i].precision() << ')';
//    if ( rs[i].nullable() )
//      std::cout << "\t null";
      std::cout << '\n';
    }

    // Print the actual results

    std::cout << "\nRows:\n";
    for ( t_query::iterator i = rs.begin(); i != rs.end(); ++i ) {
      t_query::row& r = *i;

      // Columns may be accessed by position...

      std::cout << r.get<0>() << "\t ";
            
      // ...or by name

      std::cout << (row_b)r << "\t ";
      db_double c = r.get<2>();
      if (!c) std::cout << "(null)\t ";
      else std::cout << c << "\t ";

      std::cout << r.get<3>() << std::endl;
    }
    conn.execute("drop table t");
//  conn.commit();
//  conn.disconnect();
  } catch (db_exception& e) {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}